import Visual from '../Visual';
import { WatchViewI, DrawUtilsI, BarInfo } from '../type';
import WatchNumber from '@/utils/WatchNumber';
import StringView from '../view/StringView';
import ImageView from '../view/ImageView';
import RectView from '../view/RectView';
import WatchBlock from '@/utils/WatchBlock';
import WatchValue, { WatchType } from '@/utils/WatchValue';

export type FormatValue<DATA> = (data: DATA) => string

/** 界面工具类 */
export default class ViewUtils implements DrawUtilsI {

	/** 可视化控制类 */
	private visual: Visual;

	/** 绘制界面列表 */
	private viewList: Set<WatchViewI>;

	/** 绘制主节点 */
	private block!: WatchBlock;

	/** 绘制主节点 */
	public get Block(): WatchBlock {
		return this.block;
	}

	/**
	 * 构造函数
	 * @param visual 可视化类
	 */
	public constructor(visual: Visual) {
		this.visual = visual;
		this.viewList = new Set();
	}

	/**
	 * 添加绘制项
	 * @param view 绘制项
	 */
	public Add(view: WatchViewI) {
		this.viewList.add(view);
	}

	/**
	 * 删除绘制项
	 * @param view 要删除的绘制项
	 */
	public Delete(view: WatchViewI) {
		this.viewList.delete(view);
	}

	/** @override */
	public Load() {
		this.Reset();
		this.MakeWatchBlock();
	}

	/** 重置数据 */
	private Reset() {
		this.viewList.clear();
	}

	/** 计算UI */
	private MakeWatchBlock() {
		const { visual } = this;
		const { canvas, configUtils, dataUtils: { drawLoadData } } = visual;
		const {
			padding, namePadding, topIconPadding, iconRightPadding, valueLeftPadding,
			splitFontHeight, fpsFontSize,
			rectPadding, maxLength,
		} = configUtils.config;
		const { timeList } = drawLoadData;

		const dataBoxSpace = new WatchNumber((padding, max) => padding.Value * (max.Value - 1), rectPadding, maxLength);
		const dataRectHeight = new WatchValue((max) => `${parseFloat(((1 / max.Value) * 100).toFixed(2))}%` as const, maxLength);
		const topIconSize = new WatchNumber(width => width.Value / 5, canvas.Width);
		const dateWidth = new WatchNumber(width => width.Value / 30, canvas.Width);
		const dateBoxHeight = this.MakeFormatBind(dateWidth, new WatchValue(timeList), ((item: string) => item.toString()));

		this.block = new WatchBlock("box", {
			Childrens: [
				new WatchBlock("content", {
					Childrens: [
						new WatchBlock("splitValueBox"),
						new WatchBlock("main", {
							Childrens: [
								new WatchBlock("dataBox", {
									Childrens: [
										new WatchBlock("dataRect", {
											Childrens: [
												new WatchBlock("nameBox"),
												new WatchBlock("iconBox"),
												new WatchBlock("rectBox", {
													Childrens: [
														new WatchBlock("nameInner"),
													]
												}),
												new WatchBlock("valueBox"),
											]
										}),
									]
								}),
								new WatchBlock("floatBox", {
									Childrens: [
										new WatchBlock("topIcon"),
										new WatchBlock("floatSpace"),
										new WatchBlock("dateBox"),
									]
								})
							]
						}),
						new WatchBlock("fpsBox"),
					]
				}),

			],
			StyleSheet: {
				"box": { Width: canvas.Width, Height: canvas.Height, },
				"content": { Flex: 1, Padding: padding, Direction: "row", },
				"splitValueBox": { Width: "100%", Height: splitFontHeight, },
				"main": { Flex: 1, Direction: "row", },
				"dataBox": { Width: "100%", Height: "100%", Direction: "column", },
				"dataRect": { Width: "100%", Height: -0, Direction: "column", Align: "center", },
				"nameBox": { Width: -0, Height: "85%", PaddingRight: namePadding, },
				"iconBox": { Width: -0, Height: "100%", PaddingRight: iconRightPadding, },
				"rectBox": { Flex: 1, },
				"nameInner": { Width: -0, Height: "75%", PaddingLeft: namePadding },
				"valueBox": { Width: -0, Height: "75%", PaddingLeft: valueLeftPadding },
				"floatBox": { Position: "absolute", Right: 0, Bottom: 0, Direction: "row", Align: "right", },
				"topIcon": { Width: topIconSize, Height: topIconSize, },
				"floatSpace": { Height: topIconPadding, },
				"dateBox": { Width: dateBoxHeight, Height: dateWidth, },
				"fpsBox": { Position: "absolute", Left: 0, Top: 0, Width: 0, Height: fpsFontSize },
			}
		});

		this.block.CalcUi();
		this.CalcWatchBlock();

		this.AddDate();
		this.AddTopIcon();
		this.AddFps();
		this.AddRectList();
	}

	/** 计算UI */
	private CalcWatchBlock() {
		const { visual, block } = this;
		const { configUtils, dataUtils: { drawLoadData } } = visual;
		const { formatName, formatValue, nameInLeft, iconShow, rectPadding, maxLength } = configUtils.config;
		const { rowList, valueRowList } = drawLoadData;

		const dataBoxHeight = block.QuerySelector("dataBox").Content.Height;
		const dataRect = block.QuerySelector("dataRect");
		const dataBoxSpace = new WatchNumber((padding, max) => padding.Value * (max.Value - 1), rectPadding, maxLength);
		dataRect.Style.Height.BindCall((height, space, max) =>
			(height.Value - space.Value) / max.Value
			, dataBoxHeight, dataBoxSpace, maxLength);

		const nameBox = block.QuerySelector("nameBox");
		nameBox.Style.Width.BindCall((width, inLeft) => inLeft.Value ? width.Value : 0,
			this.MakeFormatBind(nameBox.Content.Height,
				new WatchValue(rowList),
				data => configUtils.makeFormatName(data),
				formatName
			), nameInLeft);

		const nameInner = block.QuerySelector("nameInner");
		nameInner.Style.Width.Bind(this.MakeFormatBind(nameInner.Content.Height,
			new WatchValue(rowList),
			data => configUtils.makeFormatName(data),
			formatName
		));

		const iconBox = block.QuerySelector("iconBox");
		iconBox.Style.Width.BindCall((show, size) => show.Value ? size.Value : 0, iconShow, iconBox.Content.Height);

		const valueBox = block.QuerySelector("valueBox");
		valueBox.Style.Width.Bind(this.MakeFormatBind(
			valueBox.Content.Height,
			new WatchValue(() => [valueRowList.Value.reduce((x, list) => {
				const y = list.reduce((j, k) => j > k ? j : k, 0);
				return x > y ? x : y;
			}, 0)], valueRowList),
			data => configUtils.makeFormatValue(data),
			formatValue
		));
	}

	private GetTextListWidth(strList: string[]) {
		const { visual } = this;
		const { canvas } = visual;
		return Math.max(...strList.map(str => canvas.GetTextWidth(str)));
	}

	private MakeFormatBind<DATA>(size: WatchType, list: WatchValue<DATA[]>, callback: FormatValue<DATA>, ...bind: WatchType[]): WatchNumber {
		const { visual: { canvas } } = this;
		return new WatchNumber((size) => {
			canvas.Save();
			canvas.FontSize = size.Value;
			const value = this.GetTextListWidth(list.Value.map(data => callback(data)));
			canvas.Restore();
			return value;
		}, size, list, ...bind)
	}

	/** 添加日期绘制项 */
	private AddDate() {
		const { visual, block } = this;
		const { canvas, dataUtils, configUtils } = visual;
		const { drawReadData } = dataUtils;
		const { dateColor } = configUtils.config;
		const { nowDate } = drawReadData;
		const dateBox = block.QuerySelector("dateBox");
		const { Content: { Left: x, Top: y, Height: size } } = dateBox;
		const content = nowDate;
		const color = dateColor;
		const baseLine = { Value: "top" as const };
		const zIndex = { Value: 2002 };
		this.Add(new StringView(canvas, { x, y, size, content, color, borderColor: color, baseLine }, zIndex));
	}

	/** 添加第一项图标 */
	private AddTopIcon() {
		const { visual, block } = this;
		const { canvas, dataUtils, configUtils } = visual;
		const { drawReadData } = dataUtils;
		const { nowIcon } = drawReadData;
		const topIcon = block.QuerySelector("topIcon");
		const { Content: { Left: x, Top: y, Height: size } } = topIcon;
		const { iconRound } = configUtils.config;
		const radius = new WatchValue((isIcon, height) => isIcon.Value ? height.Value / 2 : null, iconRound, size);
		const zIndex = { Value: 2001 };
		this.Add(new ImageView(canvas, { x, y, width: size, height: size, icon: nowIcon, radius, }, zIndex));
	}

	/** 添加FPS显示 */
	private AddFps() {
		const { visual, block } = this;
		const { configUtils, canvas, fpsUtils } = visual;
		const { showFps, fpsColor } = configUtils.config;
		const fpsBox = block.QuerySelector("fpsBox");
		const fps = fpsUtils.Fps;
		const { Content: { Left: x, Top: y, Height: size } } = fpsBox;
		const content = new WatchValue(() => fps.Value.toFixed(), fps);
		const color = fpsColor;
		const baseLine = { Value: "top" as const };
		const zIndex = { Value: 9999 };
		const show = showFps;
		this.Add(new StringView(canvas, { x, y, size, content, color, borderColor: color, baseLine, show }, zIndex));
	}

	/** 添加数据绘制组 */
	private AddRectList() {
		const { visual, block } = this;
		const { barUtils, viewUtils, configUtils } = visual;
		const { barInfoList } = barUtils;
		const { drawRevese, iconShow, rectPadding } = configUtils.config
		const rectBox = block.QuerySelector("rectBox");
		const { Content: { Top, Width, Height } } = rectBox;
		barInfoList.forEach(info => {
			const rectY = new WatchNumber((top, height, padding, pos) => top.Value + (height.Value + padding.Value) * pos.Value, Top, Height, rectPadding, info.y.Watch);
			const rectWidth = new WatchNumber((width, widthRate) => width.Value * widthRate.Value, Width, info.width.Watch);
			const textY = new WatchNumber((y, height) => y.Value + height.Value * 0.55, rectY, Height);
			const zIndex = new WatchNumber((up) => info.y.Target * (up.Value ? -1 : 1) + 1000, drawRevese, info.y.Watch);
			viewUtils.Add(this.MakeRectView(info, rectY, rectWidth, zIndex));
			viewUtils.Add(this.MakeNameView(info, textY, zIndex));
			viewUtils.Add(this.MakeInnerNameView(info, textY, zIndex));
			viewUtils.Add(this.MakeValueView(info, rectWidth, textY, zIndex));
			let save: ImageView | null = null;
			const appendIcon = () => {
				if (save !== null) { viewUtils.Delete(save); }
				if (iconShow.Value) {
					save = this.MakeIconView(info, rectY, zIndex);
					viewUtils.Add(save);
				}
			};
			appendIcon();
			iconShow.Add("remove", appendIcon);
		});
	}

	/** 创建矩形绘制项 */
	private MakeRectView(info: BarInfo, rectY: WatchNumber, rectWidth: WatchNumber, zIndex: WatchNumber) {
		const { visual, block } = this;
		const { canvas, configUtils } = visual;
		const { rectColor } = configUtils.config;
		const rectBox = block.QuerySelector("rectBox");
		const { Content: { Left: x, Height: rectHeight } } = rectBox;
		const y = rectY;
		const width = rectWidth;
		const height = rectHeight;
		const color = new WatchValue((value, color) => value.Value || color.Value, rectColor, info.color);
		const transparency = info.transparency;
		const radius = new WatchNumber((height) => height.Value / 4, rectHeight);
		const show = info.show;
		return new RectView(canvas, { x, y, width, height, color, transparency, radius, show }, zIndex);
	}

	/** 创建名称绘制项 */
	private MakeNameView(info: BarInfo, textY: WatchNumber, zIndex: WatchNumber) {
		const { visual, block } = this;
		const { canvas, configUtils } = visual;
		const { nameColor, nameInLeft } = configUtils.config;
		const nameBox = block.QuerySelector("nameBox");
		const { Content: { Left, Width, Height: size } } = nameBox;
		const x = new WatchNumber(() => Left.Value + Width.Value, Left, Width);
		const y = textY;
		const content = info.name;
		const borderColor = new WatchValue((name, color) => name.Value || color.Value, nameColor, info.color);
		const transparency = info.transparency;
		const baseLine = { Value: "middle" as const };
		const textAlign = { Value: "right" as const };
		const lineWidth = new WatchNumber(() => size.Value * 0.03, size);
		const show = new WatchValue((show, inLeft) => show.Value && inLeft.Value, info.show, nameInLeft);
		return new StringView(canvas, { x, y, size, content, color: borderColor, borderColor, transparency, baseLine, textAlign, lineWidth, show }, zIndex);
	}

	/** 创建名称绘制项 */
	private MakeInnerNameView(info: BarInfo, textY: WatchNumber, zIndex: WatchNumber) {
		const { visual, block } = this;
		const { canvas, configUtils } = visual;
		const { nameColor, backgroundColor, nameInLeft } = configUtils.config;
		const nameInner = block.QuerySelector("nameInner");
		const { Content: { Left: x, Height: size } } = nameInner;
		const y = textY;
		const content = info.name;
		const borderColor = new WatchValue((name, color) => name.Value || color.Value, nameColor, info.color);
		const transparency = info.transparency;
		const baseLine = { Value: "middle" as const };
		const textAlign = { Value: "left" as const };
		const lineWidth = new WatchNumber(() => size.Value * 0.12, size);
		const show = new WatchValue((show, inLeft) => show.Value && !inLeft.Value, info.show, nameInLeft);
		return new StringView(canvas, { x, y, size, content, color: backgroundColor, borderColor, transparency, baseLine, textAlign, lineWidth, show }, zIndex);
	}

	/** 创建数据值绘制项 */
	private MakeValueView(info: BarInfo, rectWidth: WatchNumber, textY: WatchNumber, zIndex: WatchNumber) {
		const { visual, block } = this;
		const { canvas, configUtils } = visual;
		const { valueLeftPadding, valueColor, nameInLeft } = configUtils.config;
		const rectBox = block.QuerySelector("rectBox");
		const nameInner = block.QuerySelector("nameInner");
		const valueBox = block.QuerySelector("valueBox");
		const { Content: { Height: size } } = valueBox;
		const { Content: { Left, Width } } = nameInner;
		const x = new WatchNumber((inLeft, x1, w1, padding, x2, w2) => {
			let posX: number = x1.Value + w1.Value;
			if (!inLeft.Value) {
				const nameRight: number = x2.Value + w2.Value;
				if (posX < nameRight) {
					posX = nameRight;
				}
			}
			return posX + padding.Value;
		}, nameInLeft, rectBox.Content.Left, rectWidth, valueLeftPadding, Left, Width);
		const y = textY;
		const content = configUtils.makeFormatValueWatch(info.value.Watch);
		const color = new WatchValue((value, color) => value.Value || color.Value, valueColor, info.color);
		const transparency = info.transparency;
		const baseLine = { Value: "middle" as const };
		const textAlign = { Value: "left" as const };
		const show = info.show;
		return new StringView(canvas, { x, y, size, content, color, borderColor: color, transparency, baseLine, textAlign, show }, zIndex);
	}

	/** 创建图标绘制项 */
	private MakeIconView(info: BarInfo, y: WatchNumber, zIndex: WatchNumber): ImageView {
		const { visual, block } = this;
		const { canvas, configUtils } = visual;
		const { iconRound } = configUtils.config;
		const iconBox = block.QuerySelector("iconBox");
		const { Content: { Left: x, Height: size } } = iconBox;
		const width = size;
		const height = size;
		const icon = info.icon;
		const radius = new WatchValue((isIcon, size) => isIcon.Value ? size.Value / 2 : null, iconRound, size);
		const transparency = info.transparency;
		const show = info.show;
		return new ImageView(canvas, { x, y, width, height, icon, radius, transparency, show }, zIndex);
	}

	/** @override */
	public LoadingData() { }

	/** @override */
	public BeforeDraw() { }

	/** 绘制界面 */
	public Draw() {
		const { viewList: drawViewList } = this;
		Array.from(drawViewList).sort((x, y) => x.Z_Index.Value - y.Z_Index.Value).forEach(view => { view.Draw(); });
	}
}
